鼬~~哩賀,我是寫程式的山姆老弟,前幾天跟大家一起實驗了用 importmap、webpack、esbuild 來安裝 bootstrap,還有用 esbuild 安裝 React JS,今天來繼續延伸在 Rails 裡面搭配 React 的感覺,復刻一個之前我有做過的簡單小應用 - 圈圈叉叉,夠夠~
ps. 今天的專案就直接沿用昨天的專案。
手動新增一個 app/javascript/components/TicTacToe.jsx
的 component 檔案
// app/javascript/components/TicTacToe.jsx
import React from "react";
class TicTacToe extends React.Component {
render() {
return <div>This is TicTacToe</div>;
}
}
export default TicTacToe;
在 app/javascript/application.js
引用 TicTacToe component 並 render 出來
// app/javascript/application.js
import React from 'react';
import { createRoot } from 'react-dom/client';
import TicTacToe from './components/TicTacToe';
const container = document.getElementById('app');
createRoot(container).render(<TicTacToe/>);
在 app/views/home/index.html.erb
新增一個 div
給 react component 綁定
<!-- app/views/home/index.html.erb -->
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>
<div id="app"></div>
啟動 $ ./bin/dev
,打開 127.0.0.1:3000
,下認有下沒有成功嵌入
我的概念是把 九宮格 各自用 div 為一格來表示,我的方法比較笨,要把九個格子分別用九個 div 表達,如果有前端大神知道可以怎麼優化再麻煩留言告訴我 QQ
先把格子的版切出來,手動新增一個 wrapper
class,並使用 grid layout
/* app/assets/stylesheets/tictactoe.css */
.wrapper {
display: grid;
grid-template-columns: 100px 100px 100px;
grid-template-rows: 100px 100px 100px;
grid-gap: 20px;
align-items: center;
justify-items: center;
}
到 app/javascript/components/TicTacToe.jsx
把九宮格畫出來,並加上點擊事件(click event)
// app/javascript/components/TicTacToe.jsx
import React from "react";
class TicTacToe extends React.Component {
clicked(element) {
alert('clicked: ' + element.innerHTML)
}
render() {
return <div className="TicTacToe">
<div className="wrapper">
<div onClick={e => this.clicked(e.target)}>One</div>
<div onClick={e => this.clicked(e.target)}>Two</div>
<div onClick={e => this.clicked(e.target)}>Three</div>
<div onClick={e => this.clicked(e.target)}>Four</div>
<div onClick={e => this.clicked(e.target)}>Five</div>
<div onClick={e => this.clicked(e.target)}>Six</div>
<div onClick={e => this.clicked(e.target)}>seven</div>
<div onClick={e => this.clicked(e.target)}>eight</div>
<div onClick={e => this.clicked(e.target)}>Nine</div>
</div>
<div ref={this.winner}></div>
</div>;
}
}
export default TicTacToe;
啟動 $ ./bin/dev
,打開 127.0.0.1:3000
,確認一下點擊有沒有問題
成功~
再繼續加功能,這次把功能補齊,點擊後,要判斷輪到 O 或 X,然後把被點的字給改成 O 或 X,再判斷有沒有結束遊戲,沒有結束遊戲就繼續輪到下一位玩家,結束遊戲就顯示贏家是誰
import React from "react";
class TicTacToe extends React.Component {
constructor(props) {
super(props);
this.one = React.createRef()
this.two = React.createRef()
this.three = React.createRef()
this.four = React.createRef()
this.five = React.createRef()
this.six = React.createRef()
this.seven = React.createRef()
this.eight = React.createRef()
this.nine = React.createRef()
this.winner = React.createRef()
this.players = ['O', 'X'];
this.whosTurn = 0;
this.game = true;
this.clickableElements = [this.one, this.two, this.three, this.four, this.five, this.six, this.seven, this.eight, this.nine];
this.WIN_CONDITION = [
[1,2,3],
[4,5,6],
[7,8,9],
[1,5,9],
[3,5,7]
]
}
check() {
var result = { X: [], O: [] }
this.clickableElements.forEach ((element, index) => {
if (element.current.innerHTML === 'X') {
result.X.push(index + 1)
} else if (element.current.innerHTML === 'O') {
result.O.push(index + 1)
}
})
var winner = null
this.WIN_CONDITION.forEach ((winArr) => {
if (JSON.stringify(winArr) === JSON.stringify(result.X.sort())) {
winner = 'X'
} else if (JSON.stringify(winArr) === JSON.stringify(result.O.sort())) {
winner = 'O'
}
})
if (winner) {
return winner
}
}
clicked(clickedElement) {
if (this.game) {
var currentPlayer = this.whosTurn % this.players.length
clickedElement.innerHTML = this.players[currentPlayer]
var winner = this.check()
console.log('Winner: ' + winner)
if (winner) {
this.game = false
this.winner.current.innerHTML = 'Winner is ' + winner
}
this.whosTurn += 1
}
}
render() {
return <div className="TicTacToe">
<div className="wrapper">
<div ref={this.one} onClick={ e => this.clicked(e.target) }>One</div>
<div ref={this.two} onClick={e => this.clicked(e.target)}>Two</div>
<div ref={this.three} onClick={e => this.clicked(e.target)}>Three</div>
<div ref={this.four} onClick={e => this.clicked(e.target)}>Four</div>
<div ref={this.five} onClick={e => this.clicked(e.target)}>Five</div>
<div ref={this.six} onClick={e => this.clicked(e.target)}>Six</div>
<div ref={this.seven} onClick={e => this.clicked(e.target)}>seven</div>
<div ref={this.eight} onClick={e => this.clicked(e.target)}>eight</div>
<div ref={this.nine} onClick={e => this.clicked(e.target)}>Nine</div>
</div>
<div ref={this.winner}></div>
</div>;
}
}
export default TicTacToe;
我這邊存變數用的方法很笨,九個格子就需要九個 ref,有沒有 React 大神幫忙開導一下我 QQ,還有我對 js 不太熟,寫起來很醜,希望有大神寫下重構過的版本,留言在下面~
再次啟動 $ ./bin/dev
,打開 127.0.0.1:3000
,確認一下整個遊戲
成功~
雖然這個圈圈叉叉看起來有點銼,不過倒是讓我覺得很開心~ 竟然能在 Rails 裡使用 React,而且自己沒碰過 React 的情況下,竟然能東拼西湊出一個圈圈叉叉遊戲 XD,覺得很好玩
這接近十天連續體驗了 Rails 的各種前端方案,覺得現在對於 Rails 的前端更有點自信了,接下來我們將會回歸 RailsGuide 其他篇章,繼續探索我不熟 Rails 的部分,我們明天見~